// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <memory>
#include <string>
#include <vector>

#include <nebula/compute/function.h>
#include <nebula/compute/function_options.h>
#include <nebula/compute/type_fwd.h>

#include <turbo/utility/status.h>
#include <nebula/types/type.h>
#include <turbo/base/macros.h>

namespace nebula::compute {

    class ExecContext;

    /// \addtogroup compute-concrete-options
    /// @{

    class TURBO_EXPORT CastOptions : public FunctionOptions {
    public:
        explicit CastOptions(bool safe = true);

        static constexpr char const kTypeName[] = "CastOptions";

        static CastOptions Safe(TypeHolder to_type = {}) {
            CastOptions safe(true);
            safe.to_type = std::move(to_type);
            return safe;
        }

        static CastOptions Unsafe(TypeHolder to_type = {}) {
            CastOptions unsafe(false);
            unsafe.to_type = std::move(to_type);
            return unsafe;
        }

        // Type being casted to. May be passed separate to eager function
        // compute::Cast
        TypeHolder to_type;

        bool allow_int_overflow;
        bool allow_time_truncate;
        bool allow_time_overflow;
        bool allow_decimal_truncate;
        bool allow_float_truncate;
        // Indicate if conversions from Binary/FixedSizeBinary to string must
        // validate the utf8 payload.
        bool allow_invalid_utf8;

        /// true if the safety options all match CastOptions::Safe
        ///
        /// Note, if this returns false it does not mean is_unsafe will return true
        bool is_safe() const;

        /// true if the safety options all match CastOptions::Unsafe
        ///
        /// Note, if this returns false it does not mean is_safe will return true
        bool is_unsafe() const;
    };

    /// @}

    /// \brief Return true if a cast function is defined
    TURBO_EXPORT
    bool CanCast(const DataType &from_type, const DataType &to_type);

    // ----------------------------------------------------------------------
    // Convenience invocation APIs for a number of kernels

    /// \brief Cast from one array type to another
    /// \param[in] value array to cast
    /// \param[in] to_type type to cast to
    /// \param[in] options casting options
    /// \param[in] ctx the function execution context, optional
    /// \return the resulting array
    ///
    /// \since 1.0.0
    /// \note API not yet finalized
    TURBO_EXPORT
    turbo::Result<std::shared_ptr<Array>> Cast(const Array &value, const TypeHolder &to_type,
                                               const CastOptions &options = CastOptions::Safe(),
                                               ExecContext *ctx = nullptr);

    /// \brief Cast from one array type to another
    /// \param[in] value array to cast
    /// \param[in] options casting options. The "to_type" field must be populated
    /// \param[in] ctx the function execution context, optional
    /// \return the resulting array
    ///
    /// \since 1.0.0
    /// \note API not yet finalized
    TURBO_EXPORT
    turbo::Result<Datum> Cast(const Datum &value, const CastOptions &options,
                              ExecContext *ctx = nullptr);

    /// \brief Cast from one value to another
    /// \param[in] value datum to cast
    /// \param[in] to_type type to cast to
    /// \param[in] options casting options
    /// \param[in] ctx the function execution context, optional
    /// \return the resulting datum
    ///
    /// \since 1.0.0
    /// \note API not yet finalized
    TURBO_EXPORT
    turbo::Result<Datum> Cast(const Datum &value, const TypeHolder &to_type,
                              const CastOptions &options = CastOptions::Safe(),
                              ExecContext *ctx = nullptr);

}  // namespace nebula::compute
